1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.util.data_structures; 12 13 public import hip.util.string: String; 14 struct Pair(A, B, string aliasA = "", string aliasB = "") 15 { 16 17 A first; 18 B second; 19 20 alias a = first; 21 alias b = second; 22 23 static if(aliasA != "") 24 mixin("alias "~aliasA~" = first;"); 25 static if(aliasB != "") 26 mixin("alias "~aliasB~" = second;"); 27 } 28 struct Dirty(T) 29 { 30 T value; 31 private bool isDirty; 32 33 auto opAssign(T value) 34 { 35 if(value != this.value) this.value = value, isDirty = true; 36 return this; 37 } 38 void clearDirtyFlag(){isDirty = false;} 39 } 40 41 mixin template DirtyFlagFields(string flagName, T, string[] fields) 42 { 43 static foreach(f; fields) 44 { 45 mixin("T _",f,";", 46 "T ",f,"() => _",f,";", 47 "T ",f,"(T v){if(_",f," != v) ",flagName," = true; return _",f," = v;}"); 48 } 49 } 50 51 struct DirtyFlagsCmp(alias flag, Fields...) 52 { 53 import std.typecons; 54 static if(is(__traits(parent, Fields[0]) == struct)) 55 private static alias P = __traits(parent, Fields[0])*; 56 else 57 private static alias P = __traits(parent, Fields[0]); 58 P parent; 59 Tuple!(typeof(Fields)) base; 60 61 this(P parent) 62 { 63 start(parent); 64 } 65 66 void start(P parent) 67 { 68 this.parent = parent; 69 static foreach (i, f; Fields) 70 base[i] = __traits(child, parent, f); 71 } 72 73 void update() 74 { 75 bool changed; 76 static foreach (i, f; Fields) 77 { 78 { 79 alias T = typeof(f); 80 changed = changed || (__traits(child, parent, f) !is base[i]); 81 } 82 } 83 __traits(child, parent, flag) = __traits(child, parent, flag) || changed; 84 } 85 alias opCall = update; 86 } 87 88 89 90 /** 91 * RangeMap allows specifying a range in which a value spams, quite useful for defining outcomes 92 * based on an input, experience gain progression, etc. Example Usage: 93 * ```d 94 * RangeMap!(int, string) colorRanges = "White"; //Default is "White" 95 * colorRanges[0..9] = "Red"; 96 * colorRanges[10..19] = "Green"; 97 * colorRanges[20..29] = "Blue" 98 * 99 * writeln(colorRanges[5]); //Prints "Red" 100 * ``` 101 */ 102 struct RangeMap(K, V) 103 { 104 import std.traits:isNumeric; 105 @nogc: 106 static assert(isNumeric!K, "RangeMap key must be a numeric type"); 107 protected Array!K ranges; 108 protected Array!V values; 109 protected V _default; 110 111 /** 112 * When the value is out of range, the value returned is the _default one. 113 */ 114 ref RangeMap setDefault(V _default) 115 { 116 this._default = _default; 117 return this; 118 } 119 /** 120 * Alternative to the slice syntax 121 */ 122 ref RangeMap setRange(K a, K b, V value) 123 { 124 if(ranges == null) 125 { 126 ranges = Array!K(8); 127 values = Array!V(8); 128 } 129 int rLength = cast(int)ranges.length; 130 ranges.reserve(ranges.length+2); 131 if(a > b) 132 { 133 K temp = a; 134 a = b; 135 b = temp; 136 } 137 if(rLength != 0 && ranges[rLength-1] > a) //Silently ignore for now 138 return this; 139 ranges~=a; 140 ranges~=b; 141 values~=value; 142 return this; 143 } 144 145 /** 146 * Uses binary search for finding the value range. 147 */ 148 V get(K value) 149 { 150 int l = 0; 151 int length = cast(int)ranges.length; 152 int r = length-1; 153 154 while(l < r) 155 { 156 int m = cast(int)((l+r)/2); 157 if(m % 2 != 0) 158 m--; 159 K k = ranges[m]; 160 //Check if value is between a[m] and a[m-1] 161 if(m+1 < length && value >= k && value <= ranges[m+1]) 162 return values[cast(int)(m/2)]; 163 else if(k < value) 164 l = m + 1; 165 else if(m > value) 166 r = m - 1; 167 //Check if both values on the right is greater than value 168 else if(m+1 < length && k > value && ranges[m+1] > value) 169 break; 170 } 171 return _default; 172 } 173 174 pragma(inline) auto ref opAssign(V value) 175 { 176 setDefault(value); 177 return this; 178 } 179 pragma(inline) V opSliceAssign(V value, K start, K end) 180 { 181 setRange(start, end, value); 182 return value; 183 } 184 pragma(inline) V opIndex(K index){return get(index);} 185 } 186 /** 187 * RefCounted, Array @nogc, OutputRange compatible, it aims to bring the same result as one would have by using 188 * int[], Array!int should be equivalent, any different behaviour should be contacted. 189 * It may use more memory than requested for not making reallocation a big bottleneck 190 */ 191 struct Array(T) 192 { 193 size_t length; 194 T* data; 195 private size_t capacity; 196 private int* countPtr; 197 import core.stdc.stdlib:malloc; 198 import core.stdc.string:memcpy, memset; 199 import core.stdc.stdlib:realloc; 200 201 this(this) @nogc 202 { 203 *countPtr = *countPtr + 1; 204 } 205 alias _opApplyFn = int delegate(ref T) @nogc; 206 pragma(inline) int opApply(scope _opApplyFn dg) @nogc 207 { 208 int result = 0; 209 for(int i = 0; i < length && result; i++) 210 result = dg(data[i]); 211 return result; 212 } 213 private void initialize(size_t capacity) @nogc 214 { 215 this.data = cast(T*)malloc(T.sizeof*capacity); 216 this.capacity = capacity; 217 this.countPtr = cast(int*)malloc(int.sizeof); 218 *this.countPtr = 1; 219 this[0..capacity] = T.init; 220 } 221 222 static Array!T opCall(size_t capacity = 1) @nogc 223 { 224 Array!T ret; 225 ret.initialize(capacity); 226 return ret; 227 } 228 229 static Array!T opCall(size_t length, T value) @nogc 230 { 231 Array!T ret = Array!(T)(length); 232 ret.length = length; 233 ret[] = value; 234 return ret; 235 } 236 237 static Array!T opCall(scope T[] arr) @nogc 238 { 239 Array!T ret = Array!(T)(arr.length); 240 ret.length = arr.length; 241 memcpy(ret.data, arr.ptr, ret.length*T.sizeof); 242 return ret; 243 } 244 void* getRef() 245 { 246 *countPtr = *countPtr + 1; 247 return cast(void*)&this; 248 } 249 250 pragma(inline) bool opEquals(R)(const R other) const 251 if(is(R == typeof(null))) 252 { 253 return data == null; 254 } 255 void dispose() @nogc 256 { 257 import core.stdc.stdlib:free; 258 if(data != null) 259 { 260 free(data); 261 free(countPtr); 262 data = null; 263 countPtr = null; 264 length = capacity = 0; 265 } 266 } 267 immutable(T*) ptr(){return cast(immutable(T*))data;} 268 size_t opDollar() @nogc {return length;} 269 270 T[] opSlice() @nogc 271 { 272 return data[0..length]; 273 } 274 275 T[] opSlice(size_t start, size_t end) @nogc 276 { 277 return data[start..end]; 278 } 279 auto ref opSliceAssign(T)(T value) @nogc 280 { 281 data[0..length] = value; 282 return this; 283 } 284 auto ref opSliceAssign(T)(T value, size_t start, size_t end) @nogc 285 { 286 data[start..end] = value; 287 return this; 288 } 289 import std.traits:isArray, isNumeric; 290 auto ref opAssign(Q)(Q value) @nogc 291 if(isArray!Q) 292 { 293 if(data == null) 294 data = cast(T*)malloc(T.sizeof*value.length); 295 else 296 data = cast(T*)realloc(data, T.sizeof*value.length); 297 length = value.length; 298 capacity = value.length; 299 memcpy(data, value.ptr, T.sizeof*value.length); 300 return this; 301 } 302 303 ref auto opIndex(size_t index) @nogc 304 { 305 assert(index>= 0 && index < length, "Array out of bounds"); 306 return data[index]; 307 } 308 309 pragma(inline) private void resize(uint newSize) @nogc 310 { 311 if(data == null || capacity == 0) 312 initialize(newSize); 313 else 314 { 315 data = cast(T*)realloc(data, newSize*T.sizeof); 316 capacity = newSize; 317 } 318 } 319 ///Guarantee that no allocation will occur for the specified reserve amount of memory 320 void reserve(size_t newSize){if(newSize > capacity)resize(cast(uint)newSize);} 321 auto ref opOpAssign(string op, Q)(Q value) @nogc if(op == "~") 322 { 323 if(*countPtr != 1) 324 { 325 //Save old data 326 T* oldData = data; 327 //Remove from the ref counter 328 *countPtr = *countPtr - 1; 329 //Re initialize 330 initialize(length); 331 memcpy(data, oldData, T.sizeof*length); 332 } 333 static if(is(Q == T)) 334 { 335 if(data == null) 336 initialize(1); 337 if(length + 1 >= capacity) 338 resize(cast(uint)((length+1)*1.5)); 339 data[length++] = value; 340 } 341 else static if(is(Q == T[]) || is(Q == Array!T)) 342 { 343 if(data == null) 344 initialize(value.length); 345 if(length + value.length >= capacity) 346 resize(cast(uint)((length+value.length)*1.5)); 347 memcpy(data+length, value.ptr, T.sizeof*value.length); 348 length+= value.length; 349 } 350 return this; 351 } 352 353 String toString() @nogc 354 { 355 return String(this[0..$]); 356 } 357 void put(T data) @nogc {this~= data;} 358 ~this() @nogc 359 { 360 if(countPtr != null) 361 { 362 *countPtr = *countPtr - 1; 363 if(*countPtr <= 0) 364 dispose(); 365 countPtr = null; 366 } 367 } 368 } 369 370 371 private mixin template Array2DMixin(T) 372 { 373 int opApply(scope int delegate(ref T) dg) 374 { 375 int result = 0; 376 for(int i = 0; i < width*height; i++) 377 { 378 result = dg(data[i]); 379 if (result) 380 break; 381 } 382 return result; 383 } 384 385 private uint height; 386 private uint width; 387 @nogc int getWidth() const {return width;} 388 @nogc int getHeight() const {return height;} 389 @nogc T[] opSlice(size_t start, size_t end) 390 { 391 if(end < start) 392 { 393 size_t temp = end; 394 end = start; end = temp; 395 } 396 if(end > width*height) 397 end = width*height; 398 return data[start..end]; 399 } 400 pragma(inline, true) 401 { 402 @nogc ref auto opIndex(size_t i, size_t j){return data[i*width+j];} 403 @nogc ref auto opIndex(size_t i) 404 { 405 size_t temp = i*width; 406 return data[temp..temp+width]; 407 } 408 @nogc auto opIndexAssign(T)(T value, size_t i, size_t j){return data[i*width+j] = value;} 409 @nogc size_t opDollar() const {return width*height;} 410 @nogc bool opCast() const{return data !is null;} 411 } 412 } 413 414 /** 415 * By using Array2D instead of T[][], only one array instance is created, not "n" arrays. So it is a lot 416 * faster when you use that instead of array of arrays. 417 * 418 * Its main usage is for avoiding a[i][j] static array, and not needing to deal with array of arrays. 419 */ 420 struct Array2D(T) 421 { 422 423 mixin Array2DMixin!T; 424 Array2D_GC!T toGC() 425 { 426 Array2D_GC!T ret = new Array2D_GC!T(width, height); 427 ret.data[0..width*height] = data[0..width*height]; 428 destroy(this); 429 430 return ret; 431 } 432 @nogc: 433 private T* data; 434 import core.stdc.stdlib; 435 436 private int* countPtr; 437 438 this(this){*countPtr = *countPtr + 1;} 439 this(uint lengthHeight, uint lengthWidth) 440 { 441 data = cast(T*)malloc((lengthHeight*lengthWidth)*T.sizeof); 442 countPtr = cast(int*)malloc(int.sizeof); 443 *countPtr = 0; 444 height = lengthHeight; 445 width = lengthWidth; 446 } 447 ~this() 448 { 449 if(countPtr == null) 450 return; 451 *countPtr = *countPtr - 1; 452 if(*countPtr <= 0) 453 { 454 free(data); 455 free(countPtr); 456 data = null; 457 countPtr = null; 458 width = height = 0; 459 } 460 } 461 462 463 } 464 465 class Array2D_GC(T) 466 { 467 private T[] data; 468 this(uint lengthHeight, uint lengthWidth) 469 { 470 data = new T[](lengthHeight*lengthWidth); 471 width = lengthWidth; 472 height = lengthHeight; 473 } 474 mixin Array2DMixin!T; 475 } 476 477 private uint hash_fnv1(T)(T value) 478 { 479 import std.traits:isArray, isPointer; 480 enum fnv_offset_basis = 0x811c9dc5; 481 enum fnv_prime = 0x01000193; 482 483 byte[] data; 484 static if(isArray!T) 485 data = (cast(byte*)value.ptr)[0..value.length*T.sizeof]; 486 else static if(is(T == interface) || is(T == class) || isPointer!T) 487 data = cast(byte[])(cast(void*)value)[0..(void*).sizeof]; 488 else //Value types: int, float, struct, etc 489 data = (cast(byte*)&value)[0..T.sizeof]; 490 491 typeof(return) hash = fnv_offset_basis; 492 493 foreach(byteFromData; data) 494 { 495 hash*= fnv_prime; 496 hash^= byteFromData; 497 } 498 return hash; 499 } 500 501 struct Map(K, V) 502 { 503 import core.stdc.stdlib; 504 static enum initializationLength = 128; 505 private static enum increasingFactor = 1.5; 506 private static enum increasingThreshold = 0.7; 507 private alias hashFunc = hash_fnv1; 508 509 private struct MapData 510 { 511 K key; 512 V value; 513 } 514 private struct MapBucket 515 { 516 MapData data; 517 MapBucket* next; 518 } 519 private Array!MapBucket dataSet; 520 521 private int* countPtr; 522 523 this(this) 524 { 525 if(countPtr !is null) 526 *countPtr = *countPtr + 1; 527 } 528 529 private int entriesCount; 530 531 private void initialize() @nogc 532 { 533 dataSet = Array!(MapBucket)(initializationLength); 534 dataSet.length = initializationLength; 535 dataSet[] = MapBucket.init; 536 countPtr = cast(int*)malloc(int.sizeof); 537 *countPtr = 0; 538 } 539 static auto opCall() @nogc 540 { 541 Map!(K, V) ret; 542 ret.initialize(); 543 return ret; 544 } 545 546 547 548 void clear() @nogc 549 { 550 entriesCount = 0; 551 for(int i = 0; i < dataSet.length; i++) 552 { 553 if(dataSet[i] != MapBucket.init) 554 { 555 MapBucket* buck = dataSet[i].next; 556 while(buck != null) 557 { 558 MapBucket copy = *buck; 559 free(buck); 560 buck = copy.next; 561 } 562 } 563 } 564 } 565 //Called when array is filled at least increasingThreshold 566 private void recalculateHashes() @nogc 567 { 568 size_t newSize = cast(size_t)(dataSet.capacity * increasingFactor); 569 Array!MapBucket newArray = Array!MapBucket(newSize); 570 571 for(int i = 0; i < dataSet.length; i++) 572 { 573 if(dataSet[i] != MapBucket.init) 574 { 575 newArray[hashFunc(dataSet[i].data.key) % newSize] = dataSet[i]; 576 } 577 } 578 579 dataSet = newArray; 580 } 581 582 bool has(K key) @nogc {return dataSet[getIndex(key)] != MapBucket.init;} 583 pragma(inline) uint getIndex(K key) @nogc {return hashFunc(key) % dataSet.length;} 584 585 586 ref auto opIndex(K index) @nogc 587 { 588 if(dataSet.length == 0) 589 initialize(); 590 return dataSet[getIndex(index)].data.value; 591 } 592 ref auto opIndexAssign(V value, K key) @nogc 593 { 594 if(dataSet.length == 0) 595 initialize(); 596 uint i = getIndex(key); 597 //Unexisting bucket 598 if(dataSet[i] == MapBucket.init) 599 { 600 entriesCount++; 601 dataSet[i] = MapBucket(MapData(key, value), null); 602 if(entriesCount > dataSet.length * increasingThreshold) 603 recalculateHashes(); 604 } 605 else //Existing bucket 606 { 607 MapBucket* curr = &dataSet[i]; 608 do 609 { 610 //Check if the key is the same as the other. 611 if(curr.data.key == key) 612 { 613 curr.data.value = value; 614 return this; 615 } 616 else if(curr.next != null) 617 curr = curr.next; 618 } 619 while(curr.next != null); 620 curr.next = cast(MapBucket*)malloc(MapBucket.sizeof); 621 *curr.next = MapBucket(MapData(key, value), null); 622 623 } 624 return this; 625 } 626 627 auto opBinaryRight(string op, L)(const L lhs) const @nogc 628 if(op == "in") 629 { 630 if(dataSet.length == 0) 631 initialize(); 632 uint i = getIndex(key); 633 if(dataSet[i] == MapBucket.init) 634 return null; 635 return &dataSet[i]; 636 } 637 638 ~this() @nogc 639 { 640 if(countPtr !is null) 641 { 642 *countPtr = *countPtr - 1; 643 if(*countPtr == 0) 644 { 645 //Dispose 646 clear(); 647 free(countPtr); 648 } 649 countPtr = null; 650 } 651 } 652 653 } 654 alias AArray = Map; 655 656 struct RingBuffer(T, uint Length) 657 { 658 import hip.util.concurrency:Volatile; 659 @nogc: 660 661 T[Length] data; 662 private Volatile!uint writeCursor; 663 private Volatile!uint readCursor; 664 665 this() 666 { 667 this.writeCursor = 0; 668 this.readCursor = 0; 669 } 670 671 void push(T data) 672 { 673 this.data[writeCursor] = data; 674 writeCursor = (writeCursor+1) % Length; 675 } 676 ///It may read less than count if it is out of bounds 677 immutable T[] read(uint count) 678 { 679 uint temp = readCursor; 680 if(temp + count > Length) 681 { 682 readCursor = 0; 683 return data[temp..Length]; 684 } 685 readCursor = (temp+count)%Length; 686 return data[temp .. count]; 687 } 688 689 immutable T read() 690 { 691 uint temp = readCursor; 692 immutable T ret = data[temp]; 693 readCursor = (temp+1)%Length; 694 return ret; 695 } 696 697 void dispose() 698 { 699 data = null; 700 length = 0; 701 writeCursor = 0; 702 readCursor = 0; 703 } 704 705 ~this() 706 { 707 dispose(); 708 } 709 } 710 711 /** 712 * High efficient(at least memory-wise), tightly packed Input queue that supports any kind of data in 713 * a single allocated memory pool(no fragmentation). 714 * 715 * This class could probably be extendeded to be every event handler 716 */ 717 class EventQueue 718 { 719 import hip.util.memory; 720 @nogc: 721 722 struct Event 723 { 724 ubyte type; 725 ubyte evSize; 726 void[0] evData; 727 } 728 729 ///Linearly allocated variable length Events 730 void* eventQueue; 731 ///BytesOffset should never be greater than capacity 732 uint bytesCapacity; 733 uint bytesOffset; 734 protected uint pollCursor; 735 736 protected this(uint capacity) 737 { 738 ///Uses capacity*greatest structure size 739 bytesCapacity = cast(uint)capacity; 740 bytesOffset = 0; 741 eventQueue = malloc(bytesCapacity); 742 } 743 744 745 void post(T)(ubyte type, T ev) 746 { 747 assert(bytesOffset+T.sizeof+Event.sizeof < bytesCapacity, "InputQueue Out of bounds"); 748 if(pollCursor == bytesOffset) //It will restart if everything was polled 749 { 750 pollCursor = 0; 751 bytesOffset = 0; 752 } 753 else if(pollCursor != 0) //It will copy everything from the right to the start 754 { 755 memcpy(eventQueue, eventQueue+pollCursor, bytesOffset-pollCursor); 756 //Compensates the offset into the cursor. 757 bytesOffset-= pollCursor; 758 //Restarts the poll cursor, as everything from the right moved to left 759 pollCursor = 0; 760 } 761 Event temp; 762 temp.type = type; 763 temp.evSize = T.sizeof; 764 memcpy(eventQueue+bytesOffset, &temp, Event.sizeof); 765 memcpy(eventQueue+bytesOffset+Event.sizeof, &ev, T.sizeof); 766 bytesOffset+= T.sizeof+Event.sizeof; 767 } 768 769 void clear() 770 { 771 //By setting it equal, no one will be able to poll it 772 pollCursor = bytesOffset; 773 } 774 775 Event* poll() 776 { 777 if(bytesOffset - pollCursor <= 0) 778 return null; 779 Event* ev = cast(Event*)(eventQueue+pollCursor); 780 pollCursor+= ev.evSize+Event.sizeof; 781 return ev; 782 } 783 784 protected ~this() 785 { 786 free(eventQueue); 787 eventQueue = null; 788 pollCursor = 0; 789 bytesCapacity = 0; 790 bytesOffset = 0; 791 } 792 } 793 794 class Node(T) 795 { 796 T data; 797 Node!(T)[] children; 798 Node!T parent; 799 800 this(T data){this.data = data;} 801 802 pragma(inline) bool isRoot() const @nogc nothrow {return parent is null;} 803 pragma(inline) bool hasChildren() const @nogc nothrow {return children.length != 0;} 804 Node!T get(T data) 805 { 806 foreach(node; this) 807 { 808 if(node.data == data) 809 return node; 810 } 811 return null; 812 } 813 pragma(inline) Node!T addChild(T data) 814 { 815 Node!T ret = new Node(data); 816 ret.parent = this; 817 children~= ret; 818 return ret; 819 } 820 821 Node!T getRoot() 822 { 823 Node!T ret = this; 824 while(ret.parent !is null) 825 ret = ret.parent; 826 return ret; 827 } 828 829 int opApply(scope int delegate(Node!T) cb) 830 { 831 foreach(child; children) 832 { 833 if(cb(child) || child.opApply(cb)) 834 return 1; 835 } 836 return 0; 837 } 838 } 839 840 struct Signal(A...) 841 { 842 Array!(void function(A)) listeners; 843 void dispatch(A a) 844 { 845 foreach (void function(A) l; listeners) 846 l(a); 847 } 848 }